<?php
/**
 * Created by PhpStorm.
 * User: YONBIN
 * Date: 2020/10/16
 * Time: 16:55
 * Email: 479703456@qq.com
 */

namespace app\domain\common;


use app\model\Config;
use Exception;

class UploaderDomain extends CommonDomain
{
    /**
     * 分片上传（如果文件已经上传过，返回文件的路径）
     * @param $type 资源保存位置
     * @param $chunkNumber 当前块的次序，第一个块是 1，注意不是从 0 开始的。
     * @param $chunkSize 分块大小，根据 totalSize 和这个值你就可以计算出总共的块数（注意最后一块的大小可能会比这个要大），如：2048000。
     * @param $currentChunkSize 当前块的大小，实际大小，如：2048000。
     * @param $totalSize 文件总大小，如：5284731。
     * @param $identifier 这个就是每个文件的唯一标示（MD5），如：70f643c706abe9cf17fc5591f59e5d10。
     * @param $filename 文件名。
     * @param $relativePath 文件夹上传的时候文件的相对路径属性。
     * @param $totalChunks 文件被分成块的总片数，如：2。
     * @return array
     */
    public function uploader_chunk($type, $chunkNumber, $chunkSize, $currentChunkSize, $totalSize, $identifier, $filename, $relativePath, $totalChunks)
    {
        /*
        chunkNumber:1 当前块的次序，第一个块是 1，注意不是从 0 开始的。
        totalChunks:2 文件被分成块的总数。
        chunkSize:2048000 分块大小，根据 totalSize 和这个值你就可以计算出总共的块数。注意最后一块的大小可能会比这个要大。
        currentChunkSize:2048000 当前块的大小，实际大小。
        totalSize:5284731 文件总大小。
        identifier:70f643c706abe9cf17fc5591f59e5d10 这个就是每个文件的唯一标示。
        filename:20160716204629_AkvJu.jpeg 文件名。
        relativePath:20160716204629_AkvJu.jpeg 文件夹上传的时候文件的相对路径属性。
        */

        $allowDirs = array_keys(config('common.upload_path'));
        $baseDir = in_array($type, $allowDirs) ? config('config.upload_path')[$type] : config('config.upload_path')['thumb'];
        $baseDir = ltrim($baseDir, './');

        $suffix = strrchr($filename, '.');
        if (empty($_FILES)) {
            //+-------------------------------------------------------------------------------------------------
            //|分片上传预检：（每个文件在前端点击开始上传时发出的请求）
            //|1、只携带分片参数（第一片），没有文件数据流；
            //|2、这里只能通过文件唯一标识检测完整的文件是否上传过；
            //+-------------------------------------------------------------------------------------------------
            $file_search = model('api/VideoList')::withAttr('path', function ($value) {
                return $value;
            })->removeOption('soft_delete')->getByName($identifier . $suffix);
            $check = $file_search && file_exists("./{$file_search->path}");

            return [
                'tips' => '这里返回的是资源的引用路径',
                'vid' => $check ? $file_search->id : 0,
                'skipUpload' => (bool)$check,
                'url' => isset($file_search['path']) ? config('common.serverUrl') . $file_search['path'] : ''
            ];
        } else {
            //+-------------------------------------------------------------------------------------------------
            //|分片上传：
            //|1、携带当前分片参数和文件数据流；
            //|2、根据文件唯一标识比对分片是否上传和分片完整性；完整上传的回传“skipUpload”跳过上传；不完整的分片将删除重传；
            //+-------------------------------------------------------------------------------------------------
            $skipUpload = false;
            $filePath = "{$baseDir}{$identifier}/{$identifier}_part{$chunkNumber}";
            if (file_exists($filePath) && $currentChunkSize === filesize($filePath)) {
                $this->errMsg = lang('upload.chunk_uploaded', ['number' => $chunkNumber]);

                //+------------------------------------------------------
                //|如果是最后一片就继续执行：
                //|1、合并文件出错导致分片存在但合并文件不存在；
                //+------------------------------------------------------
                if ($totalChunks === $chunkNumber) {
                    $skipUpload = true;
                } else {
                    return ['skipUpload' => true];
                }
            }

            if (file_exists($filePath) && $currentChunkSize != filesize($filePath)) {
                unlink($filePath);
            }

            if (!file_exists($baseDir . $identifier)) @mkdir($baseDir . $identifier, 0777, true);

            if (false === $skipUpload) {
                $move = move_uploaded_file($_FILES['file']['tmp_name'], $filePath);
            } else {
                $move = true;
            }

            if ($chunkNumber !== $totalChunks) {
                if ($move) {
                    $this->errMsg = lang('upload.chunk_upload_success', ['number' => $chunkNumber]);
                } else {
                    $this->errMsg = lang('upload.chunk_upload_failure', ['number' => $chunkNumber]);
                }
            }

            //合并文件
            if ($totalChunks === $chunkNumber) {
                //+-----------------------------------
                //|合并文件前先把已有的同名文件删除
                //|避免追加写入导致文件体积无限变大
                //+-----------------------------------
                if (file_exists($baseDir . $identifier . $suffix)) @unlink($baseDir . $identifier . $suffix);

                $newFileName = $identifier . $suffix;
                for ($i = 1; $i <= $totalChunks; $i++) {
                    $chunkPath = "{$baseDir}{$identifier}/{$identifier}_part{$i}";
                    $data = file_get_contents($chunkPath);
                    $fput = file_put_contents($baseDir . $newFileName, $data, FILE_APPEND);

                    if ($fput) unlink($chunkPath);
                }

                $fileSize = filesize($baseDir . $newFileName);
                if ($fileSize == $totalSize) {
                    @rmdir($baseDir . $identifier);

                    $this->errMsg = lang('upload.success');
                    return [
                        'url' => config('config.serverUrl') . $baseDir . $newFileName
                    ];
                }
            }
        }
    }

    /**
     * 通用图片上传
     * @param string $type 通过上传类型来区分上传文件保存位置
     * @return array|bool
     */
    public function uploader_common(string $type = 'avatar')
    {
        try {
            if (empty($_FILES['file'])) return false;

            $mimeTypes = [
                'image/jpeg',
                'image/png',
                'image/gif',
                'video/mp4',
                'application/msword',   //DOC
                'application/vnd.ms-excel', //XLS
                'application/x-tar',    //TAR
                'application/pdf',   //PDF
                'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',    //xlsx
            ];

            $mimeType = $_FILES['file']['type'];
            if (!in_array($mimeType, $mimeTypes)) {
                $this->errMsg = '类型不允许(' . implode(',', $mimeTypes) . ')';
                return false;
            }

            /*
            $open = finfo_open(FILEINFO_MIME_TYPE);
            $mimeType = finfo_file($open, $basePath);
            if (!in_array($mimeType, ['image/jpeg', 'image/pjpeg', 'image/jpeg', 'image/png', 'image/x-png'])) {
                throw new Exception('该类型文件禁止访问！');
            }
            */

            $dirs = config('common.upload_path');
            $dirKeys = array_keys($dirs);
            $type = in_array($type, $dirKeys) ? $type : 'attachment';
            $baseDir = ltrim($dirs[$type], './') . date('Y-m-d') . '/';

            if (!file_exists($baseDir)) @mkdir($baseDir, 0777, true);

            $suffix = strrchr($_FILES['file']['name'], '.');
            $newFileName = md5_file($_FILES['file']['tmp_name']) . $suffix;

            $move = move_uploaded_file($_FILES['file']['tmp_name'], $baseDir . $newFileName);
            if ($move) {
                $config = app(Config::class)->getByName('server_base_url');
                $domain = $config->value ?? env('BASE_URI');
                return [
                    'url' => $domain . $baseDir . $newFileName,
                    'mimetype' => $mimeType
                ];
            } else {
                return false;
            }
        } catch (\Exception $e) {
            $this->errMsg = $e->getMessage();
            return false;
        }
    }
}